package com.ruoyi.business.modbus.serial;

import com.ruoyi.business.modbus.serial.jserialcomm.JSerialCommSerialPortWrapper;
import com.serotonin.modbus4j.serial.SerialPortWrapper;
import com.ruoyi.business.modbus.domain.ModbusMasterDevice;
import com.ruoyi.business.modbus.domain.ModbusRtuMasterDevice;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 串口包装器工厂类
 * 
 * @author ruoyi
 * @date 2024-01-01
 */
public class SerialPortWrapperFactory {
    
    private static final Logger log = LoggerFactory.getLogger(SerialPortWrapperFactory.class);
    
    // 使用jSerialComm库作为唯一实现
    private static final String LIBRARY_NAME = "jSerialComm";
    
    /**
     * 根据设备信息创建串口包装器
     * 
     * @param device 设备信息
     * @return 串口包装器
     * @throws Exception 创建失败时抛出异常
     */
    public static SerialPortWrapper createSerialPortWrapper(ModbusMasterDevice device) throws RuntimeException {
        validateDevice(device);
        
        if (!(device instanceof ModbusRtuMasterDevice)) {
            throw new IllegalArgumentException("设备类型必须是ModbusRtuMasterDevice");
        }
        
        ModbusRtuMasterDevice rtuDevice = (ModbusRtuMasterDevice) device;
        
        log.info("为设备 {} 创建{}串口包装器: 端口={}, 波特率={}, 数据位={}, 停止位={}, 校验位={}", 
                 rtuDevice.getDeviceName(), LIBRARY_NAME, rtuDevice.getSerialPort(), rtuDevice.getBaudRate(), 
                 rtuDevice.getDataBits(), rtuDevice.getStopBits(), rtuDevice.getParity());
        
        return new JSerialCommSerialPortWrapper(
            rtuDevice.getSerialPort(),
            rtuDevice.getBaudRate(),
            rtuDevice.getDataBits(),
            rtuDevice.getStopBits(),
            convertParityStringToInt(rtuDevice.getParity())
        );
    }
    
    /**
     * 验证设备串口配置
     * 
     * @param device 设备信息
     * @throws IllegalArgumentException 配置无效时抛出异常
     */
    private static void validateDevice(ModbusMasterDevice device) throws IllegalArgumentException {
        if (device == null) {
            throw new IllegalArgumentException("设备信息不能为null");
        }
        
        if (!(device instanceof ModbusRtuMasterDevice)) {
            throw new IllegalArgumentException("设备类型必须是ModbusRtuMasterDevice");
        }
        
        ModbusRtuMasterDevice rtuDevice = (ModbusRtuMasterDevice) device;
        
        if (rtuDevice.getSerialPort() == null || rtuDevice.getSerialPort().trim().isEmpty()) {
            throw new IllegalArgumentException("串口端口不能为空");
        }
        
        if (rtuDevice.getBaudRate() == null || rtuDevice.getBaudRate() <= 0) {
            throw new IllegalArgumentException("波特率必须大于0");
        }
        
        if (rtuDevice.getDataBits() == null || (rtuDevice.getDataBits() < 5 || rtuDevice.getDataBits() > 8)) {
            throw new IllegalArgumentException("数据位必须在5-8之间");
        }
        
        if (rtuDevice.getStopBits() == null || (rtuDevice.getStopBits() < 1 || rtuDevice.getStopBits() > 2)) {
            throw new IllegalArgumentException("停止位必须为1或2");
        }
        
        if (rtuDevice.getParity() == null || rtuDevice.getParity().trim().isEmpty()) {
            throw new IllegalArgumentException("校验位不能为空");
        }
    }
    
    /**
     * 将校验位字符串转换为整数
     * 
     * @param parity 校验位字符串 (NONE, ODD, EVEN, MARK, SPACE)
     * @return 校验位整数值
     */
    private static int convertParityStringToInt(String parity) {
        if (parity == null) {
            return 0; // NONE
        }
        
        switch (parity.toUpperCase().trim()) {
            case "NONE":
                return 0;
            case "ODD":
                return 1;
            case "EVEN":
                return 2;
            case "MARK":
                return 3;
            case "SPACE":
                return 4;
            default:
                log.warn("未知的校验位类型: {}, 使用默认值NONE", parity);
                return 0;
        }
    }
    
    /**
     * 获取支持的串口列表
     * 
     * @return 可用的串口名称数组
     */
    public static String[] getAvailableSerialPorts() {
        try {
            com.fazecast.jSerialComm.SerialPort[] ports = com.fazecast.jSerialComm.SerialPort.getCommPorts();
            String[] portNames = new String[ports.length];
            for (int i = 0; i < ports.length; i++) {
                portNames[i] = ports[i].getSystemPortName();
            }
            return portNames;
        } catch (Exception e) {
            log.error("使用{}获取串口列表失败: {}", LIBRARY_NAME, e.getMessage());
            return new String[0];
        }
    }
    
    /**
     * 检查指定串口是否可用
     * 
     * @param portName 串口名称
     * @return 是否可用
     */
    public static boolean isPortAvailable(String portName) {
        if (portName == null || portName.trim().isEmpty()) {
            return false;
        }
        
        String[] availablePorts = getAvailableSerialPorts();
        for (String port : availablePorts) {
            if (portName.equals(port)) {
                return true;
            }
        }
        return false;
    }
    
    /**
     * 测试串口连接
     * 
     * @param device 设备信息
     * @return 连接测试结果
     */
    public static boolean testSerialPortConnection(ModbusMasterDevice device) {
        if (!(device instanceof ModbusRtuMasterDevice)) {
            log.error("设备类型必须是ModbusRtuMasterDevice，当前类型: {}", device.getClass().getSimpleName());
            return false;
        }
        
        ModbusRtuMasterDevice rtuDevice = (ModbusRtuMasterDevice) device;
        SerialPortWrapper wrapper = null;
        try {
            wrapper = createSerialPortWrapper(device);
            wrapper.open();
            log.info("使用{}库串口 {} 连接测试成功", LIBRARY_NAME, rtuDevice.getSerialPort());
            return true;
        } catch (Exception e) {
            log.error("使用{}库串口 {} 连接测试失败: {}", LIBRARY_NAME, rtuDevice.getSerialPort(), e.getMessage());
            return false;
        } finally {
            if (wrapper != null) {
                try {
                    wrapper.close();
                } catch (Exception e) {
                    log.warn("关闭测试连接失败: {}", e.getMessage());
                }
            }
        }
    }
    
    /**
     * 获取当前使用的串口库名称
     * 
     * @return 串口库名称
     */
    public static String getLibraryName() {
        return LIBRARY_NAME;
    }
    
    /**
     * 检查串口库是否可用
     * 
     * @return 是否可用
     */
    public static boolean isLibraryAvailable() {
        try {
            // 尝试获取jSerialComm串口列表
            com.fazecast.jSerialComm.SerialPort.getCommPorts();
            return true;
        } catch (Exception e) {
            log.debug("串口库 {} 不可用: {}", LIBRARY_NAME, e.getMessage());
            return false;
        }
    }
}
